package fr.uparis10.miage.m1p1.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;

import fr.uparis10.miage.classic.m1.p3.Departement;
import fr.uparis10.miage.m1p1.core.Employe;
import fr.uparis10.miage.m1p1.core.PerformanceEmploye;
import fr.uparis10.miage.m1p1.core.Projet;
import fr.uparis10.miage.m1p1.core.Statut;
import fr.uparis10.miage.m1p1.employes.ChefProjet;
import fr.uparis10.miage.m1p1.employes.DirecteurDepartement;
import fr.uparis10.miage.m1p1.employes.DirecteurProjet;
import fr.uparis10.miage.m1p1.employes.Ingenieur;
import fr.uparis10.miage.m1p1.employes.Secretaire;
import fr.uparis10.miage.m1p1.employes.TechnicoCommercial;
import fr.uparis10.miage.m1p1.exceptions.ProjetException;
import fr.uparis10.miage.m1p1.global.EmplTypeEnum;

public class EmployeReader extends PropertyReader {
	private static final String PROJECT_SEPARATOR_REGEXP = ";";
	private static EmployeReader theInstance = null;
	
	/**
	 * Private Constructor - singleton class
	 */
	private EmployeReader() {
	}
	
	private ArrayList<Projet> buildProjetArray(String listeProjStr) {
		ArrayList<Projet> projets = new ArrayList<Projet>();
		if (listeProjStr == null) {
			return projets;
		}
		ArrayList<Projet> projetListe = Configurator.getInstance().getProjetListe();
		HashMap<String, Projet> projetMap = Configurator.getInstance().getProjetMap();
		String[] projNames = listeProjStr.split(PROJECT_SEPARATOR_REGEXP);
		for (String projName: projNames) {
			// skip null or empty; in case of "split" this can arrive
			if (projName == null || projName.length() == 0) {
				continue;
			}
			Projet aProj = projetMap.get(projName);
			if (aProj == null) {
				// No such project, create a dummy one
				aProj = new Projet(
							projName,
							new ArrayList<String>(),
							0f,
							0d,
							Calendar.getInstance().getTime(),
							Calendar.getInstance().getTime());
				// playing dirty here:
				projetListe.add(aProj);
				projetMap.put(projName, aProj);
			}
			projets.add(aProj);
		}
		return projets;
	}
	
	public static EmployeReader getInstance() {
		if (theInstance == null) {
			theInstance = new EmployeReader(); 
		}
		return theInstance;
	}
	
	public ArrayList<Employe> readEmployes(File aFile) {
		Properties prop = new Properties();
		FileInputStream inStream;
		
		ArrayList <Employe> employeListe = new ArrayList<Employe>();
		
		HashMap<String, Departement> departementMap = Configurator.getInstance().getDepartementMap();
		ArrayList<Departement> departementListe = Configurator.getInstance().getDepartementListe();
		
		try {
			inStream = new FileInputStream(aFile);
			prop.load(inStream);
		} catch (FileNotFoundException exc) {
			log.log(Level.SEVERE, "Exception occured: ", exc);
			return employeListe;
		} catch (IOException exc) {
			log.log(Level.SEVERE, "Exception occured: ", exc);
			return employeListe;
		}
		int empCount = getIntProperty(prop, "employe_count", -1);
		if (empCount <= 0) {
			log.log(Level.SEVERE, "Invalid employe_count=" + empCount + ". Return...");
			return employeListe;
		}
		SimpleDateFormat dateFormat;
		try {
			Object tmp = prop.get("date_format");
			dateFormat = new SimpleDateFormat(tmp.toString());
		} catch (NullPointerException exc) {
			log.log(Level.WARNING, "Unspecified date_format", exc);
			// default
			dateFormat = new SimpleDateFormat("yyyy/mm/dd");
		} catch (IllegalArgumentException exc) {
			log.log(Level.WARNING, "Invalid date_format", exc);
			// default
			dateFormat = new SimpleDateFormat("yyyy/mm/dd");
		}

		// Start to read all the employees data
		for (int i = 0; i < empCount; i++) {
			String tmp;
			/* Start to read & convert */
			tmp = getStringProperty(prop, "empl_type_" + i, "Ingenieur");
			EmplTypeEnum empType = EmplTypeEnum.valueOf(tmp);
			int empId = getIntProperty(prop, "empl_id_" + i, 0);
			long empNSS = getLongProperty(prop, "empl_nss_" + i, 0L);
			Date empJoinDate = getDateProperty(prop, "empl_join_date_" + i, dateFormat);
			String empName = getStringProperty(prop, "empl_name_" + i, "");
			String empAddress = getStringProperty(prop, "empl_address_" + i, "");
			Date empBirthDate = getDateProperty(prop, "empl_birth_date_" + i, dateFormat);
			tmp = getStringProperty(prop, "empl_status_" + i, "ACTIVE");
			Statut empStatus = Statut.valueOf(tmp);
			tmp = getStringProperty(prop, "empl_dept_" + i, "");
			Departement empDept = departementMap.get(tmp);
			if (empDept == null) {
				// the Departement doesn't exist; lets create one ;-)
				empDept = new Departement(tmp);
				departementListe.add(empDept);
				departementMap.put(tmp, empDept);
			}
			float empSalary = getFloatProperty(prop, "empl_salary_" + i, 0f);
			tmp = getStringProperty(prop, "empl_performance_" + i, "C");
			PerformanceEmploye empPerfInd = PerformanceEmploye.valueOf(tmp);
			/* End of read & convert*/

			/* Create the Employe object */
			Employe empl;
			try {
				double budget;
				Date startDate;
				String listeProjStr;
				ArrayList<Projet> projets;
				switch (empType) {
				case ChefProjet:
					budget = getDoubleProperty(prop, "empl_manager_budget_" + i, 0d);
					startDate = getDateProperty(prop, "empl_manager_start_date_" + i, dateFormat);
					listeProjStr = prop.getProperty("empl_liste_projets_" + i);
					projets = buildProjetArray(listeProjStr);
					
					ChefProjet aChef = new ChefProjet(empId, empNSS,
							empJoinDate, budget, startDate);
					for (Projet aProj: projets) {
						aChef.ajouterProjet(aProj);
					}
					empl = aChef;
					break;
				case DirecteurDepartement:
					budget = getDoubleProperty(prop, "empl_manager_budget_" + i, 0d);
					startDate = getDateProperty(prop, "empl_manager_start_date_" + i, dateFormat);
					DirecteurDepartement aDirDept = new DirecteurDepartement(empId, empNSS,
							empJoinDate, budget, startDate);
					empl = aDirDept;
					break;
				case DirecteurProjet:
					budget = getDoubleProperty(prop, "empl_manager_budget_" + i, 0d);
					startDate = getDateProperty(prop, "empl_manager_start_date_" + i, dateFormat);
					listeProjStr = prop.getProperty("empl_liste_projets_" + i);
					projets = buildProjetArray(listeProjStr);

					DirecteurProjet aDirProj = new DirecteurProjet(empId, empNSS,
							empJoinDate, budget, startDate);
					for (Projet aProj: projets) {
						try {
							aDirProj.suivreProjet(aProj);
						} catch (ProjetException exc) {
							log.log(Level.INFO, "The Director can't follow this project:", exc);
						}
					}
					empl = aDirProj;
					break;
				case Ingenieur:
					listeProjStr = prop.getProperty("empl_liste_projets_" + i);
					projets = buildProjetArray(listeProjStr);

					Ingenieur aIng = new Ingenieur(empId, empNSS, empJoinDate);

					for (Projet aProj: projets) {
						aIng.setProjet(aProj);
					}
					empl = aIng;
					break;
				case Secretaire:
					empl = new Secretaire(empId, empNSS, empJoinDate);
					break;
				case TechnicoCommercial:
					listeProjStr = prop.getProperty("empl_liste_projets_" + i);
					projets = buildProjetArray(listeProjStr);

					TechnicoCommercial aSalesMan = new TechnicoCommercial(empId, empNSS, empJoinDate);

					for (Projet aProj: projets) {
						aSalesMan.ajouterProjet(aProj);
					}
					empl = aSalesMan;
					break;
				default:
					/* Shouldn't ever arrive to this point, but just in case*/
					continue;
				}
				/* Generic parameters - START */
				empl.setNom(empName);
				empl.setAdresse(empAddress);
				empl.setDateNaissance(empBirthDate);
				empl.setStatut(empStatus);
				empl.setDepartement(empDept);
				empl.setSalaire(empSalary);
				empl.setIndice(empPerfInd);
				/* Generic parameters - END */
				
				/* Add the new employe to the list */
				employeListe.add(empl);
				
				/* Patch to set the projects of a department */
				boolean exist = false;
				ArrayList<Projet> proj = empl.getProjetList();
				for (Projet p : proj) {
					ArrayList<Projet> projDept = empDept.getProjet();
					for (Projet pr : projDept)
						if (exist = p.equals(pr))
							break;
					if (!exist)
						empDept.addProjet(p);
				}

			} catch (NullPointerException exc) {
				/* Who knows what arrives? log the exception and continue */
				log.log(Level.WARNING, "Exception occured: ", exc);
				continue;
			}
		}
		
		return employeListe;
	}

	

}
